package node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import log.Logger;

/**
 * This class is responsible for computing the connections between nodes.
 * @author Team4
 * @version Spring 2013
 *
 */
public class NodeConnections
{
  /**
   * The hashmap to store the connection channel.
   */
  private static Map<Integer, ArrayList<Integer>> my_node_connections = 
      new HashMap<Integer, ArrayList<Integer>>();
 
  /**
   * The total number of nodes.
   */
  private int my_total_nodes;



  /**
   * Instantiates the total number of nodes and connection channel.
   * 
   * @param the_total_nodes_chord  total number of visual node in the ring
   */
  public NodeConnections(final int the_total_nodes_chord)
  {
    my_total_nodes = the_total_nodes_chord;
    this.my_node_connections = getconnectmap(my_total_nodes); 
  }
  /**
   * This method computes all the neighbours for the given node id. It stores them in 
   * a array list.
   * @param the_node_number - the node id
   * @param the_total_nodes_inchord - the total number of nodes in this chord
   * @return 
   * It simply finds nodes 1 node away, 2 nodes away and 4 nodes away .....
   */
  public static List<Integer> findConnections(final int the_node_number, 
                                                    final int the_total_nodes_inchord)
  {
    double power = Math.log10(the_total_nodes_inchord) / Math.log10(2);
    power = (int) power;
    final List<Integer> to_return = new ArrayList<Integer>();
    for (int i = 0; i < power; i++)
    {
      to_return.add((the_node_number + (int) Math.pow(2, i)) % 
                    the_total_nodes_inchord);            
    }
    return (ArrayList<Integer>) to_return;    
  }

  /**
   * Create a hashmap to store the node channel in the ring.
   * @param the_total_number  total number of nodes
   * @return the hash map.
   */
  public Map<Integer, ArrayList<Integer>> getconnectmap(final int the_total_number)
  { 
    final Map<Integer, ArrayList<Integer>> to_return = new 
        HashMap<Integer, ArrayList<Integer>>();
    for (int tempnumber = 0; tempnumber < the_total_number; tempnumber++)
    {
      List<Integer> templist = new ArrayList<Integer>();
      templist = findConnections(tempnumber, the_total_number);
      to_return.put(tempnumber, (ArrayList<Integer>) templist);      
    }  
    return (HashMap<Integer, ArrayList<Integer>>) to_return;    
  }

  /**
   * Print the hashmap of the ring.
   */
  public void printout()
  {
    Map<Integer, ArrayList<Integer>> to_print = new HashMap<Integer, ArrayList<Integer>>();
    to_print = this.my_node_connections;
    final Iterator<Integer> tempiterator = to_print.keySet().iterator();   
    while (tempiterator.hasNext())
    {
      final int tempnumber = tempiterator.next();
      final List<Integer> templist = (ArrayList<Integer>) to_print.get(tempnumber);
      final Iterator<Integer> tempiterator2 = templist.iterator();
      final StringBuffer sb = new StringBuffer();
      sb.append("Node No." + tempnumber + " is connected to : ");
      while (tempiterator2.hasNext())
      {
        sb.append("Node ");
        final int temp2 = tempiterator2.next();
        sb.append(temp2);
        sb.append(", ");
      }
      Logger.out(sb.toString());
//      Logger.writeContent(NodeManager.LOGGER_FILE_NAME, sb.toString());
    }
  }
  /**
   * This prints out the connections to one node.
   * @param the_node_number ---the number of current node
   * @param the_total_nodes ---the total number of nodes.
   */
  public static void printoutOneNode(final int the_node_number, final int the_total_nodes)
  {
    List<Integer> to_print = new ArrayList<Integer>();
    to_print =  findConnections(the_node_number, the_total_nodes);
    final Iterator<Integer> tempiterator = to_print.iterator();   
    final StringBuffer sb = new StringBuffer();
    sb.append(" Node " + the_node_number + " is connected to ");
    while (tempiterator.hasNext())
    {
      final int tempnumber = tempiterator.next();
      sb.append(tempnumber);
      sb.append(" ");
    }
    Logger.out(sb.toString());
  }
  /**
   * Given a node number , this method will find what other nodes will communicate with this node.
   * Useful for queue processing
   */
  public void reverseConnections(int the_node_num)
  {
    double power = Math.log10(NodeManager.total_nodes_chord) / Math.log10(2);
    power = (int) power;
    final List<Integer> to_return = new ArrayList<Integer>();
    for (int i = 0; i < power; i++)
    {
      to_return.add((the_node_num - (int) Math.pow(2, i)) % 
                    NodeManager.total_nodes_chord);            
    }
    //return (ArrayList<Integer>) to_return;   
  }

}
